import Vue from 'vue'
import 'es6-promise/auto'
import { createApp } from './app'
import ProgressBar from './components/ProgressBar.vue'

// global progress bar
// 全局进度条
const bar = Vue.prototype.$bar = new Vue(ProgressBar).$mount() // 将组件挂载到body上
document.body.appendChild(bar.$el) // 将组件的dom添加到body上

// a global mixin that calls `asyncData` when a route component's params change
Vue.mixin({
   // 在当前路由改变，但是该组件被复用时调用
   // 可以访问组件实例 `this`
  beforeRouteUpdate (to, from, next) {
    console.log('RouteUpdate', to.fullPath)
    const { asyncData } = this.$options
    if (asyncData) {
      asyncData({
        store: this.$store,
        route: to
      }).then(next).catch(next)
    } else {
      next()
    }
  }
})
/* TODO:
  就是在某个路由页面切换页面的时候，比如/top/1 -> /top/2
  此时beforeRouteUpdate和beforeResolve钩子都会执行，而且
  虽然用的都是同一个name的组件，但是在beforeResolve钩子的时候判定
  matched和prevMatched始终是不相等的，但是这两个是两个列表，里面装的是
  匹配到的路由组件，因为内部路由组件是相同的，所以asyncData只会执行一次
*/

const { app, router, store } = createApp()

// prime the store with server-initialized state.
// the state is determined during SSR and inlined in the page markup.
// 使用服务器初始化的状态填充store
// 状态在服务器端渲染(SSR)期间确定，并内联在页面标记中
if (window.__INITIAL_STATE__) {
  store.replaceState(window.__INITIAL_STATE__)
}

// wait until router has resolved all async before hooks
// and async components...
// 等待路由解析所有的异步before钩子和异步组件...
router.onReady(() => {
  // Add router hook for handling asyncData.
  // Doing it after initial route is resolved so that we don't double-fetch
  // the data that we already have. Using router.beforeResolve() so that all
  // async components are resolved.
  // 添加路由钩子来处理asyncData
  // 在初始路由 TODO:解析后 执行，以避免重复获取已有的数据
  // 使用router.beforeResolve()确保所有异步组件都已解析
  // TODO:
  // 一旦页面被加载到浏览器中，Vue 会接管页面，使其变成一个单页面应用（SPA）。
  // 此后的所有路由导航，包括 URL 地址的变化，都会在客户端处理，不会再向服务器发送请求。
  // 这个过程中，客户端的路由钩子（如 router.beforeResolve）会被执行。
  router.beforeResolve((to, from, next) => { // 第一次客户端加载路由的时候不去重新执行asyncData
    console.log('beforeResolve', to.fullPath)
    const matched = router.getMatchedComponents(to)
    const prevMatched = router.getMatchedComponents(from)
    console.log('matched', matched) // length === 1
    console.log('prevMatched', prevMatched) // length === 1
    console.log(matched[0] === prevMatched[0]) // 为true
    let diffed = false
    const activated = matched.filter((c, i) => {
      return diffed || (diffed = (prevMatched[i] !== c))
    })
    // 如果当前路由组件和上次不一样，那么会依次重新执行当前路径下路由组件的asyncData
    const asyncDataHooks = activated.map(c => c.asyncData).filter(_ => _)
    if (!asyncDataHooks.length) {
      return next()
    }

    bar.start()
    Promise.all(asyncDataHooks.map(hook => hook({ store, route: to })))
      .then(() => {
        bar.finish()
        next() // 加载完后才切换路由
      })
      .catch(next)
  })

  // actually mount to DOM
  app.$mount('#app') // 初次所有路由加载完毕后，调用app.$mount('#app')挂载到dom上
})

// service worker
if ('https:' === location.protocol && navigator.serviceWorker) {
  navigator.serviceWorker.register('/service-worker.js')
}
